#include "broot_force.h"
#include <iostream>
#include <vector>
#include <boost/tuple/tuple.hpp>
#undef assert
#include "assert.h"

using std::cerr;
using std::auto_ptr;
using std::endl;
using std::vector;
using std::string;
using std::min;

void BrootForce::createCheckers()
{
    finalCheckers.push_back(new FinalSymmetricChecker());
    finalCheckers.push_back(new FinalDimensionChecker());
    finalCheckers.push_back(new FinalBiparateChecker());
    finalCheckers.push_back(new FinalNaiveColoringChecker());
}

void BrootForce::createModifiers()
{
    CombineChecker* vertexModifierChecker = new CombineChecker();
    vertexModifierChecker->addChecker(new FinalSymmetricChecker());
    vertexModifierChecker->addChecker(new OnlineSymmetricCoordChecker());

    modifiers.push_back(new VertexModifier(vertexModifierChecker));
    modifiers.push_back(new DegreeModifier());
    modifiers.push_back(new DimensionModifier());
    modifiers.push_back(new ColoringModifier());
}

string fname()
{
    static int x = 0;
    char res[100];
    sprintf(res, "%.4d", x++);
    return res;
}

void BrootForce::run(size_t v)
{
    if (v >= SIZE_VMSK) {
        Graph g(state.dist, state.dist,
            vector<size_t>(state.vertecies,
                           state.vertecies + state.numVertecies));
        state.subGraph = &g;
        for(size_t i = 0; i < finalCheckers.size(); ++i) {
            if (!finalCheckers[i].check(state)) {
                return ;
            }
        }
        string f = fname();
        cerr << f << endl;
        draw(&g, f);
        state.subGraph = 0;
        return ;
    }
    bool alreadyNotGetted = state.getted[v] == 0;
    if (alreadyNotGetted) {
        statusBar.breakBrunch(v);
        run(v + 1);
        return ;
    }    
    UndoStack undoStack;
    if (processEvent(Event(GET_VERTEX, v), undoStack)) {
        run(v + 1);
    } else {
        statusBar.breakBrunch(v);
    }
    processUndoStack(undoStack);

    if (processEvent(Event(NOT_GET_VERTEX, v), undoStack)) {
        run(v + 1);
    } else {
        statusBar.breakBrunch(v);
    }
    processUndoStack(undoStack);
}

bool BrootForce::processEvent(Event event, UndoStack& undoStack)
{
    EventQueue eventQueue;
    eventQueue.push(event);
    return processEventQueue(eventQueue, undoStack);
}

bool BrootForce::processEventQueue(EventQueue& eventQueue, UndoStack& undoStack)
{
    while (!eventQueue.empty()) {
        Event event = eventQueue.front();
        eventQueue.pop();
        size_t lastModifier = runModifiers(event, modifiers.size(), eventQueue);
        undoStack.push(
            std::make_pair(Event(Event::undoType(event.type), event.vertex),
                      lastModifier));
        if (lastModifier != modifiers.size()) {
            return false;
        }
    }
    return true;
}

bool BrootForce::processUndoStack(UndoStack& undoStack)
{
    bool res = true;
    EventQueue eventQueue;
    while (!undoStack.empty()) {
        Event event;
        size_t lastModifier;
        boost::tie(event, lastModifier) = undoStack.top();
        undoStack.pop();
        runModifiers(event, lastModifier, eventQueue);
    }
    assert(eventQueue.empty(), "eventQueue.empty()");
    return res;
}

size_t BrootForce::runModifiers(Event event,
                                size_t lastModifier,
                                EventQueue& eventQueue)
{
    size_t v = event.vertex;
    for(size_t i = 0; i < min(modifiers.size(), lastModifier + 1); ++i) {
        IModifier& modifier = modifiers[i];
        switch (event.type) {
            case GET_VERTEX:
                if (!modifier.getVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case UNDO_GET_VERTEX:
                if (!modifier.undoGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case NOT_GET_VERTEX:
                if (!modifier.notGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case UNDO_NOT_GET_VERTEX:
                if (!modifier.undoNotGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            default:
                assert(false, "???");
        }
    }
    return modifiers.size();
}
